/**
 * <p>IJPay 让支付触手可及，封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
 *
 * <p>不依赖任何第三方 mvc 框架，仅仅作为工具使用简单快速完成支付模块的开发，可轻松嵌入到任何系统里。 </p>
 *
 * <p>IJPay 交流群: 723992875</p>
 *
 * <p>Node.js 版: https://gitee.com/javen205/TNWX</p>
 *
 * <p>Model 公用方法</p>
 *
 * @author Javen
 */
package org.finegold.core.pay.unionpay.model;


import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import org.finegold.core.pay.unionpay.enums.TradeType;
import org.finegold.core.tool.utils.Base64Util;
import org.finegold.core.tool.utils.DigestUtil;
import org.finegold.core.tool.utils.Func;
import org.finegold.core.pay.common.core.IBaseModel;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class BaseModel implements IBaseModel {

    /**
     * 将建构的 builder 转为 Map
     *
     * @return 转化后的 Map
     */
    @Override
    public Map<String, String> toMap() {
        String[] fieldNames = getFiledNames(this);
        HashMap<String, String> map = new HashMap<String, String>(fieldNames.length);
        for (String name : fieldNames) {
            String value = (String) getFieldValueByName(name, this);
            if (Func.isNotEmpty(value)) {
                map.put(name, value);
            }
        }

        map.put("merOrderId",map.get("billNo"));
        return map;
    }

    /**
     * 构建签名 Map
     *
     * @return 构建签名后的 Map
     */
    @SneakyThrows
	@Override
	public Map<String,Map<String, String>>  createSign(UnionModel model) {
		//时间戳
		String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());   // eg:20190227113148
		//随机数
		String nonce = UUID.randomUUID().toString().replace("-", ""); // eg:be46cd581c9f46ecbd71b9858311ea12
		Map<String, String> stringMap = this.toMap();
		if(Func.isNotEmpty(model)){

			stringMap= model.toMap();
		}
		Map<String, String> map1 = Maps.newHashMap();
		map1.putAll(stringMap);
		map1.remove("lybh");
		map1.remove("severUrl");
		map1.remove("appId");
		map1.remove("appKey");

		String jsonObject = JSONObject.toJSONString(map1);
		String bodyDigest = DigestUtil.sha256Hex(jsonObject);
		String str1_C = stringMap.get("appId") + timestamp + nonce + bodyDigest; // eg:f0ec96ad2c3848b5b810e7aadf369e2f + 20190227113148 + be46cd581c9f46ecbd71b9858311ea12 + d60bc3aedeb853e2a11c0c096baaf19954dd9b752e48dea8e919e5fb29a42a8d
		byte[] localSignature = hmacSHA256(str1_C.getBytes(), stringMap.get("appKey").getBytes());
		String localSignatureStr = Base64Util.encodeToString(localSignature);
		Map<String,Map<String, String>> mapR= Maps.newHashMap();
		Map<String, String> map = Maps.newHashMap();
		if(stringMap.get("instMid").equals(TradeType.ALIH5DEFAULT.getTradeType())){
			map.put("url",stringMap.get("severUrl")+("?authorization=OPEN-FORM-PARAM" + "&appId=" + stringMap.get("appId") + "&timestamp=" + timestamp + "&nonce=" + nonce + "&content="
				+ URLEncoder.encode(jsonObject.toString(), "UTF-8") + "&signature=" + URLEncoder.encode(localSignatureStr, "UTF-8")));
			mapR.put("returnUrl",map);
		}else if(stringMap.get("instMid").equals(TradeType.QRPAYDEFAULT.getTradeType())){
			map.put("Authorization",("OPEN-BODY-SIG AppId=" + "\"" + stringMap.get("appId") + "\"" + ", Timestamp=" + "\"" + timestamp + "\"" + ", Nonce=" + "\"" + nonce + "\"" + ", Signature=" + "\"" + localSignatureStr + "\""));
			mapR.put("head",map);
			mapR.put("body", stringMap);
			mapR.put("json", map1);
		}else if(stringMap.get("instMid").equals(TradeType.ALIAPPDEFAULT.getTradeType())){
			map.put("Authorization",("OPEN-BODY-SIG AppId=" + "\"" + stringMap.get("appId") + "\"" + ", Timestamp=" + "\"" + timestamp + "\"" + ", Nonce=" + "\"" + nonce + "\"" + ", Signature=" + "\"" + localSignatureStr + "\""));
			mapR.put("head",map);
			mapR.put("body", stringMap);
			mapR.put("json", map1);
		}

		return mapR;
    }

	/**
	 * @param data
	 * @param key
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 */
	private static byte[] hmacSHA256(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
		String algorithm = "HmacSHA256";
		Mac mac = Mac.getInstance(algorithm);
		mac.init(new SecretKeySpec(key, algorithm));
		return mac.doFinal(data);
	}

    /**
     * 获取属性名数组
     *
     * @param obj 对象
     * @return 返回对象属性名数组
     */
	@Override
    public String[] getFiledNames(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldNames[i] = fields[i].getName();
        }
        return fieldNames;
    }

    /**
     * 根据属性名获取属性值
     *
     * @param fieldName 属性名称
     * @param obj       对象
     * @return 返回对应属性的值
     */
	@Override
    public Object getFieldValueByName(String fieldName, Object obj) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = new StringBuffer().append("get")
                    .append(firstLetter)
                    .append(fieldName.substring(1))
                    .toString();
            Method method = obj.getClass().getMethod(getter);
            return method.invoke(obj);
        } catch (Exception e) {
            return null;
        }
    }

}
